import random
import string
import time

from django.db import models
from django.db.models import Q
from django.contrib.auth.models import AbstractBaseUser
from django.db.models.constraints import UniqueConstraint, CheckConstraint
from django.utils.functional import cached_property

from gongyequ import choices


class UserManager(models.Manager):
    def create_user(self, password=None, **extra_fields):
        user = self.model(**extra_fields)
        # 此处仅针对于使用手机号验证码登录用户
        if 'username' not in extra_fields:
            user.username = self.generate_username()
        user.set_password(password)
        user.save()

        return user

    def generate_username(self):
        """生成随机昵称
        昵称格式：'gongyequ_' + 时间相关的随机数 + 10以内3为随机数(10的3次方)
        * 除以 60 之后拼接3位随机数，每分钟最多可以注册1000人，保证空间足够大
        * 除以 60 之后拼接3位随机数，使用时间戳和随机数来相互约制，保证每分钟生成的昵称都是唯一性的
        * 以上考虑的点在我们的体系中应该足够了，后续视情况而优化
        """
        username = 'gongyequ_' + str(int(time.time() / 60)) + ''.join(random.choices(string.digits, k=3))

        return username


class User(AbstractBaseUser):
    class Meta:
        constraints = [
            # 要求手机号必须唯一
            UniqueConstraint(fields=['phone'], name='%(app_label)s_%(class)s_unique_phone'),

            # 要求邮箱必须唯一
            UniqueConstraint(fields=['email'], name='%(app_label)s_%(class)s_unique_email'),

            # 要求 username、phone、email 不能全都为 NULL
            CheckConstraint(check=~Q(username=None, phone=None, email=None),
                            name='%(app_label)s_%(class)s_must_have_identity'),

            # 要求 username、phone、email 不能全都为 空字符串
            CheckConstraint(check=~Q(Q(username='') | Q(phone='') | Q(email='')),
                            name='%(app_label)s_%(class)s_identity_must_not_be_empty_string'),
        ]

    objects = UserManager()
    SEX = choices.User.SEX

    username = models.TextField(unique=True, default=None, verbose_name='用户名')
    phone = models.TextField(null=True, default=None, verbose_name='手机号')
    phone_validated = models.BooleanField(default=False, verbose_name='手机号是否已验证')

    # 性别
    sex = models.IntegerField(choices=SEX.choices, default=SEX.unknown, verbose_name='性别')
    avatar = models.TextField(blank=True, null=True, verbose_name='头像URL')

    # 防止后续有邮箱注册
    email = models.TextField(null=True, default=None, verbose_name='邮箱')
    email_validated = models.BooleanField(default=False, verbose_name='邮箱是否已验证')

    # 标记是否是管理员
    is_admin = models.BooleanField(default=False)
    # 用户是否被锁定
    is_active = models.BooleanField(default=True)

    # 删除用户 软删除
    is_delete = models.BooleanField(default=True)

    # 该字段由 Django 的 AbstractBaseUser 中的方法使用，我们自己不建议使用，总是忽略
    USERNAME_FIELD = 'username'

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        r = f'id={self.id} username={self.username} phone={self.phone}'
        return r

    @cached_property
    def permissions(self):
        if self.is_admin:
            return ['admin']
        else:
            return []

    def has_permission(self, perm):
        return perm in self.permissions

    def serialize(self):
        data = dict(
            id=self.id,
            username=self.username,
            phone=self.phone,
            is_admin=self.is_admin,
            is_active=self.is_active,
            created_at=self.created_at,
            updated_at=self.updated_at,
        )
        return data

    def can_authenticate(self):
        # 用于标注用户是否被锁定或是已经被删除
        return self.is_active and self.is_delete